"""
文章生成服务
"""
import time
from typing import List, Optional
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, and_
from sqlalchemy.orm import selectinload
from app.models.article import Article, ArticleStatus
from app.models.media import MediaAsset, MediaType
from app.models.theme import Theme
from app.schemas.article import (
    ArticleGenerateRequest, ArticleResponse, ArticleListResponse,
    ArticleGenerationResult
)
from app.services.deepseek_service import deepseek_service
from app.services.tongyi_service import tongyi_service
from app.services.wechat_service import wechat_service
from app.services.theme_service import theme_service
from app.utils.exceptions import (
    DatabaseError, ContentGenerationError, ImageGenerationError,
    ThemeNotFoundError, ArticleNotFoundError
)
from app.utils.logger import logger
from app.utils.helpers import (
    generate_uuid, insert_images_into_content, insert_images_into_wechat_content, format_wechat_content
)


class ArticleService:
    """文章生成服务"""
    
    async def generate_article(self, db: AsyncSession, 
                             request: ArticleGenerateRequest) -> ArticleGenerationResult:
        """生成文章"""
        start_time = time.time()
        
        try:
            # 获取主题信息 (短数据库操作)
            theme = await theme_service.get_theme_by_id(db, request.theme_id)
            theme_name = str(theme.name)
            theme_keywords = theme.keywords or []
            # 使用配置中的最小长度，而不是主题中的content_length（避免数据库中的旧值影响）
            from app.config import app_config
            theme_content_length = app_config.generation["content_min_length"]
            theme_id = str(theme.id)
            
            logger.info(f"开始生成文章，主题: {theme_name}")
            
            # 在数据库事务外执行DeepSeek API调用
            logger.info("开始调用DeepSeek API生成内容...")
            
            # 生成标题
            logger.info("正在生成标题...")
            try:
                title = await deepseek_service.generate_title(
                    theme_name, theme_keywords
                )
            except Exception as title_error:
                logger.error(f"标题生成失败: {str(title_error)}")
                # 使用备用标题
                title = f"关于{theme_name}的深度解析"
                logger.warning(f"使用备用标题: {title}")
            
            # 验证标题是否有效
            if not title or not title.strip():
                logger.warning("生成的标题为空，使用默认标题")
                title = f"基于{theme_name}的文章"
            
            title = title.strip()
            if len(title) > 60:
                title = title[:57] + "..."
                
            logger.info(f"标题生成完成: {title}")
            
            # 生成摘要
            logger.info("正在生成摘要...")
            try:
                summary = await deepseek_service.generate_summary(
                    title, theme_name
                )
            except Exception as summary_error:
                logger.error(f"摘要生成失败: {str(summary_error)}")
                # 使用备用摘要
                summary = f"本文深入探讨{theme_name}相关话题，为您提供全面的分析和见解。"
                logger.warning(f"使用备用摘要: {summary}")
            
            # 验证摘要
            if summary:
                summary = summary.strip()
                if len(summary) > 100:
                    summary = summary[:97] + "..."
            else:
                summary = f"探索{theme_name}的精彩世界"
                
            logger.info(f"摘要生成完成: {summary[:50]}...")
            
            # 生成内容
            logger.info("正在生成文章内容...")
            try:
                content = await deepseek_service.generate_content(
                    title, theme_name, theme_content_length
                )
            except Exception as content_error:
                logger.error(f"内容生成失败: {str(content_error)}")
                # 创建备用内容
                content = f"""# {title}

> {summary}

## 深入了解{theme_name}

{theme_name}是当今备受关注的重要话题。通过深入分析，我们可以发现其中蕴含的丰富内容和价值。

## 核心要点分析

**重要观点**：{theme_name}具有重要的现实意义和发展前景。

- 第一个要点：深入理解{theme_name}的核心概念
- 第二个要点：掌握{theme_name}的实际应用
- 第三个要点：把握{theme_name}的发展趋势

## 实践指导

在实际应用中，我们需要：

1. **系统学习**：全面了解{theme_name}的基础知识
2. **实践探索**：通过实际操作加深理解
3. **持续关注**：保持对{theme_name}发展的关注

## 总结与展望

{theme_name}作为重要话题，值得我们深入研究和探讨。通过本文的分析，相信读者已经对{theme_name}有了更全面的认识。

未来，{theme_name}必将迎来更大的发展机遇，让我们共同期待其美好的发展前景。"""
                logger.warning(f"使用备用内容，长度: {len(content)}字符")
            
            # 验证内容
            if not content or not content.strip():
                logger.error("生成的内容为空")
                raise ValueError("生成的内容为空，请重试")
            
            content = content.strip()
            if len(content) < 100:
                logger.warning(f"生成的内容过短（{len(content)}字符），将进行扩展")
                # 可以在这里添加内容扩展逻辑
                if len(content) < 50:
                    raise ValueError("生成的内容太短，请重试")
            
            logger.info(f"内容生成完成，长度: {len(content)}字符")
            
            # 立即应用微信富文本格式化
            logger.info("正在应用微信富文本格式化...")
            try:
                formatted_content = format_wechat_content(content)
                logger.info(f"格式化完成，处理后长度: {len(formatted_content)}字符")
            except Exception as format_error:
                logger.error(f"格式化失败: {str(format_error)}")
                # 如果格式化失败，使用原始内容
                formatted_content = content
            
            # 现在开始数据库操作
            logger.info("开始保存文章到数据库...")
            
            # 创建文章记录
            article = Article(
                id=generate_uuid(),
                theme_id=theme_id,
                title=title,
                summary=summary,
                content=formatted_content,  # 使用格式化后的内容
                status=ArticleStatus.DRAFT
            )
            
            db.add(article)
            # 先提交文章内容，避免长时间占用连接
            await db.commit()
            await db.refresh(article)
            
            logger.info(f"文章内容已保存，ID: {article.id}")
            
            # 生成图片 (这部分可能需要更长时间，使用已保存的文章)
            if request.include_images:
                try:
                    # 重新获取文章以确保连接有效
                    await self._generate_and_upload_images(
                        db, article, request.num_content_images
                    )
                    # 图片生成后再次提交
                    await db.commit()
                    await db.refresh(article)
                except Exception as e:
                    logger.error(f"图片生成失败，但文章内容已保存: {str(e)}")
                    # 图片生成失败不影响文章保存，但需要刷新文章
                    try:
                        await db.refresh(article)
                    except Exception:
                        # 如果刷新失败，重新查询文章
                        result = await db.execute(
                            select(Article).where(Article.id == article.id)
                        )
                        article = result.scalar_one()
            
            # 重新查询以确保所有关联数据都正确加载
            result = await db.execute(
                select(Article)
                .options(selectinload(Article.media_assets))
                .where(Article.id == article.id)
            )
            article_with_assets = result.scalar_one()
            
            generation_time = time.time() - start_time
            
            logger.info(f"文章生成完成，用时: {generation_time:.2f}秒")
            
            return ArticleGenerationResult(
                article=ArticleResponse.model_validate(article_with_assets),
                success=True,
                message="文章生成成功",
                generation_time=generation_time
            )
            
        except Exception as e:
            await db.rollback()
            generation_time = time.time() - start_time
            error_msg = f"文章生成失败: {str(e)}"
            logger.error(error_msg)
            
            return ArticleGenerationResult(
                article=None,
                success=False,
                message=error_msg,
                generation_time=generation_time
            )
    
    async def _generate_and_process_images(self, article_id: str, title: str, theme_name: str, 
                                          content: str, num_content_images: int):
        """生成和处理图片（不在数据库事务中）"""
        cover_media_data = None
        content_media_data = []
        
        try:
            # 生成封面图片
            logger.info("开始生成封面图片...")
            cover_description = await deepseek_service.generate_cover_image_prompt(
                title, theme_name
            )
            
            cover_image_url = await tongyi_service.generate_cover_image(
                title, cover_description
            )
            
            # 上传封面图片到微信
            cover_result = await wechat_service.upload_image_from_url(cover_image_url)
            if isinstance(cover_result, dict):
                cover_media_data = cover_result
            else:
                # 兼容旧格式
                cover_media_data = {"media_id": cover_result, "url": cover_result}
            logger.info(f"封面图片上传成功: {cover_media_data}")
            
            # 生成内容配图
            if num_content_images > 0:
                logger.info(f"开始生成{num_content_images}张内容配图...")
                image_descriptions = await deepseek_service.generate_image_prompts(
                    content, num_content_images
                )
                
                content_image_urls = await tongyi_service.generate_content_images(
                    image_descriptions
                )
                
                # 上传内容图片
                for i, image_url in enumerate(content_image_urls):
                    try:
                        result = await wechat_service.upload_image_from_url(image_url)
                        if isinstance(result, dict):
                            content_media_data.append(result)
                        else:
                            # 兼容旧格式
                            content_media_data.append({"media_id": result, "url": result})
                        logger.info(f"第{i+1}张配图上传成功: {result}")
                    except Exception as e:
                        logger.error(f"上传第{i+1}张配图失败: {str(e)}")
                        continue
                        
            return cover_media_data, content_media_data
            
        except Exception as e:
            logger.error(f"图片生成和处理失败: {str(e)}")
            # 返回部分成功的结果
            return None, []
    
    async def _save_media_assets(self, db: AsyncSession, article_id: str, 
                               cover_media_data: dict, content_media_data: list):
        """保存媒体素材记录到数据库"""
        try:
            # 保存封面素材记录
            if cover_media_data:
                cover_asset = MediaAsset(
                    id=generate_uuid(),
                    article_id=article_id,
                    media_id=cover_media_data.get("media_id", ""),
                    original_url=cover_media_data.get("url", ""),  # 保存CDN URL
                    media_type=MediaType.IMAGE,
                    position=0
                )
                db.add(cover_asset)
            
            # 保存内容图片素材记录
            for i, media_data in enumerate(content_media_data):
                media_asset = MediaAsset(
                    id=generate_uuid(),
                    article_id=article_id,
                    media_id=media_data.get("media_id", ""),
                    original_url=media_data.get("url", ""),  # 保存CDN URL
                    media_type=MediaType.IMAGE,
                    position=i + 1
                )
                db.add(media_asset)
                
        except Exception as e:
            logger.error(f"保存媒体素材记录失败: {str(e)}")
            raise
    
    async def _generate_and_upload_images(self, db: AsyncSession, article: Article, num_content_images: int):
        """生成并上传图片（保留兼容性）"""
        # 调用新的分离式处理方法
        cover_media_data, content_media_data = await self._generate_and_process_images(
            str(article.id), str(article.title), 
            str(article.theme.name) if hasattr(article, 'theme') and article.theme else "",
            str(article.content), num_content_images
        )
        
        # 更新文章封面
        if cover_media_data:
            article.cover_image_id = cover_media_data.get("media_id", "")
        
        # 保存媒体素材记录
        await self._save_media_assets(db, str(article.id), cover_media_data, content_media_data)
        
        # 将图片插入到内容中
        if content_media_data:
            # 使用完整的图片数据，包含CDN URL
            updated_content = insert_images_into_wechat_content(
                str(article.content), content_media_data
            )
            article.content = updated_content
        
        logger.info(f"图片生成和上传完成，封面: {cover_media_data.get('media_id', '') if cover_media_data else ''}, 配图: {len(content_media_data)}张")
    
    async def get_article_by_id(self, db: AsyncSession, article_id: str) -> Article:
        """根据ID获取文章"""
        try:
            result = await db.execute(
                select(Article)
                .options(selectinload(Article.media_assets), selectinload(Article.theme))
                .where(Article.id == article_id)
            )
            article = result.scalar_one_or_none()
            
            if not article:
                raise ArticleNotFoundError(article_id)
            
            return article
            
        except ArticleNotFoundError:
            raise
        except Exception as e:
            logger.error(f"查询文章失败: {str(e)}")
            raise DatabaseError(f"查询文章失败: {e}")
    
    async def get_articles(self, db: AsyncSession, skip: int = 0, limit: int = 20,
                          theme_id: Optional[str] = None, 
                          status: Optional[ArticleStatus] = None) -> ArticleListResponse:
        """获取文章列表"""
        try:
            # 构建查询条件
            where_conditions = []
            if theme_id:
                where_conditions.append(Article.theme_id == theme_id)
            if status:
                where_conditions.append(Article.status == status)
            
            # 查询总数
            count_query = select(func.count(Article.id))
            if where_conditions:
                count_query = count_query.where(and_(*where_conditions))
            
            total_result = await db.execute(count_query)
            total = total_result.scalar() or 0
            
            # 查询文章列表
            query = select(Article).options(
                selectinload(Article.media_assets)
            ).order_by(Article.generated_at.desc())
            
            if where_conditions:
                query = query.where(and_(*where_conditions))
            
            query = query.offset(skip).limit(limit)
            result = await db.execute(query)
            articles = result.scalars().all()
            
            # 计算分页信息
            page = (skip // limit) + 1 if limit > 0 else 1
            has_next = (skip + len(articles)) < total
            
            # 安全转换文章数据，过滤掉验证失败的记录
            valid_articles = []
            invalid_count = 0
            
            for article in articles:
                try:
                    # 检查关键字段是否有效
                    title_value = getattr(article, 'title', '')
                    content_value = getattr(article, 'content', '')
                    
                    if not title_value or not str(title_value).strip():
                        logger.warning(f"跳过标题为空的文章 ID: {getattr(article, 'id', 'unknown')}")
                        invalid_count += 1
                        continue
                    
                    if not content_value or not str(content_value).strip():
                        logger.warning(f"跳过内容为空的文章 ID: {getattr(article, 'id', 'unknown')}")
                        invalid_count += 1
                        continue
                    
                    # 尝试验证并转换
                    article_response = ArticleResponse.model_validate(article)
                    valid_articles.append(article_response)
                    
                except Exception as e:
                    logger.warning(f"跳过无效文章 ID: {getattr(article, 'id', 'unknown')}, 错误: {str(e)}")
                    invalid_count += 1
                    continue
            
            if invalid_count > 0:
                logger.warning(f"发现 {invalid_count} 条无效文章记录，已自动过滤")
            
            logger.info(f"获取文章列表成功，共 {total} 条记录，有效 {len(valid_articles)} 条")
            
            return ArticleListResponse(
                items=valid_articles,
                total=len(valid_articles),  # 使用有效文章数量
                page=page,
                page_size=limit,
                has_next=has_next
            )
            
        except Exception as e:
            logger.error(f"获取文章列表失败: {str(e)}")
            raise DatabaseError(f"获取文章列表失败: {e}")
    
    async def delete_article(self, db: AsyncSession, article_id: str) -> bool:
        """删除文章"""
        try:
            article = await self.get_article_by_id(db, article_id)
            
            # 如果有草稿ID，尝试删除微信草稿
            if article.draft_id:
                try:
                    await wechat_service.delete_draft(str(article.draft_id))
                except Exception as e:
                    logger.warning(f"删除微信草稿失败: {str(e)}")
            
            await db.delete(article)
            await db.commit()
            
            logger.info(f"删除文章成功: {article.title} (ID: {article_id})")
            return True
            
        except ArticleNotFoundError:
            raise
        except Exception as e:
            await db.rollback()
            logger.error(f"删除文章失败: {str(e)}")
            raise DatabaseError(f"删除文章失败: {e}")
    
    async def update_article_status(self, db: AsyncSession, article_id: str, 
                                  status: ArticleStatus, draft_id: Optional[str] = None) -> Article:
        """更新文章状态"""
        try:
            article = await self.get_article_by_id(db, article_id)
            
            article.status = status
            if draft_id:
                article.draft_id = draft_id
            
            if status == ArticleStatus.PUBLISHED:
                from datetime import datetime
                article.published_at = datetime.now()
            
            await db.commit()
            await db.refresh(article)
            
            logger.info(f"更新文章状态成功: {article.title} -> {status.value}")
            return article
            
        except ArticleNotFoundError:
            raise
        except Exception as e:
            await db.rollback()
            logger.error(f"更新文章状态失败: {str(e)}")
            raise DatabaseError(f"更新文章状态失败: {e}")


# 全局服务实例
article_service = ArticleService()